package com.vinny.springbootflowable2.service.impl;

import com.vinny.springbootflowable2.common.constant.Constants;
import com.vinny.springbootflowable2.exception.BusinessException;
import com.vinny.springbootflowable2.mapper.ExpenseMapper;
import com.vinny.springbootflowable2.model.dto.ExpenseDTO;
import com.vinny.springbootflowable2.model.entity.Expense;
import com.vinny.springbootflowable2.model.enums.ExpenseStatus;
import com.vinny.springbootflowable2.model.vo.ActivityInfoVO;
import com.vinny.springbootflowable2.model.vo.ProcessDiagramVO;
import com.vinny.springbootflowable2.service.ExpenseService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class ExpenseServiceImpl implements ExpenseService {

    private final RuntimeService runtimeService;
    private final TaskService taskService;
    private final HistoryService historyService;
    private final RepositoryService repositoryService;
    private final ProcessEngine processEngine;
    private final ExpenseMapper expenseMapper;

    @Override
    @Transactional
    public String submitExpense(String userId, ExpenseDTO expenseDTO) {
        // 创建报销记录
        Expense expense = new Expense();
        expense.setUserId(userId);
        expense.setMoney(expenseDTO.getMoney());
        expense.setDescription(expenseDTO.getDescription());
        expense.setStatus(ExpenseStatus.SUBMITTED.getCode());

        // 启动流程
        Map<String, Object> variables = new HashMap<>();
        variables.put("money", expenseDTO.getMoney());
        variables.put("description", expenseDTO.getDescription());

        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
                Constants.PROCESS_KEY, 
                variables
        );

        // 更新报销记录
        expense.setProcessInstanceId(processInstance.getId());
        expenseMapper.insert(expense);

        return processInstance.getId();
    }

    @Override
    @Transactional(readOnly = true)
    public List<Task> getUserTasks(String userId) {
        return taskService.createTaskQuery()
                .taskCandidateOrAssigned(userId)
                .orderByTaskCreateTime().desc()
                .list();
    }

    @Override
    @Transactional
    public void approveTask(String userId, String taskId) {
        Task task = getTask(taskId);
        checkTaskPermission(task, userId);

        // 更新报销记录状态
        updateExpenseStatus(task.getProcessInstanceId(), ExpenseStatus.APPROVED);

        Map<String, Object> variables = new HashMap<>();
        variables.put("outcome", Constants.OUTCOME_PASS);
        taskService.complete(taskId, variables);
    }

    @Override
    @Transactional
    public void rejectTask(String userId, String taskId) {
        Task task = getTask(taskId);
        checkTaskPermission(task, userId);

        // 更新报销记录状态
        updateExpenseStatus(task.getProcessInstanceId(), ExpenseStatus.REJECTED);

        Map<String, Object> variables = new HashMap<>();
        variables.put("outcome", Constants.OUTCOME_REJECT);
        taskService.complete(taskId, variables);
    }

    @Override
    @Transactional(readOnly = true)
    public byte[] generateProcessDiagram(String processId) {
        try {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processId)
                    .singleResult();
            
            String processDefinitionId;
            if (processInstance == null) {
                HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                        .processInstanceId(processId)
                        .singleResult();
                if (historicProcessInstance == null) {
                    throw new BusinessException("流程实例不存在");
                }
                processDefinitionId = historicProcessInstance.getProcessDefinitionId();
            } else {
                processDefinitionId = processInstance.getProcessDefinitionId();
            }

            // 获取流程图
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
            ProcessDiagramGenerator diagramGenerator = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();

            // 获取高亮节点
            List<String> activeActivityIds = new ArrayList<>();
            List<String> highLightedFlows = new ArrayList<>();
            
            if (processInstance != null) {
                activeActivityIds = runtimeService.getActiveActivityIds(processId);
            }

            // 获取已完成的节点
            List<String> completedActivityIds = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(processId)
                    .finished()
                    .list()
                    .stream()
                    .map(HistoricActivityInstance::getActivityId)
                    .collect(Collectors.toList());
                    
            // 合并活动节点和已完成节点
            completedActivityIds.addAll(activeActivityIds);

            // 生成流程图
            try (InputStream inputStream = diagramGenerator.generateDiagram(
                    bpmnModel,
                    "png",
                    completedActivityIds,
                    highLightedFlows,
                    "宋体",
                    "宋体",
                    "宋体",
                    null,
                    1.0,
                    false)) {

                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int length;
                while ((length = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, length);
                }
                return outputStream.toByteArray();
            }
        } catch (IOException e) {
            log.error("生成流程图失败", e);
            throw new BusinessException("生成流程图失败");
        }
    }

    @Override
    public ProcessDiagramVO getProcessDiagramInfo(String processId) {
        // 获取流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processId)
                .singleResult();
        
        if (processInstance == null) {
            // 查询历史流程实例
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(processId)
                    .singleResult();
            if (historicProcessInstance == null) {
                throw new BusinessException("流程实例不存在");
            }
        }

        // 获取流程图
        byte[] processImage = generateProcessDiagram(processId);
        String base64Image = Base64.getEncoder().encodeToString(processImage);

        // 获取当前活动的节点
        List<String> activeActivityIds = new ArrayList<>();
        if (processInstance != null) {
            activeActivityIds = runtimeService.getActiveActivityIds(processId);
        }

        // 获取已完成的节点
        List<String> completedActivityIds = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processId)
                .finished()
                .list()
                .stream()
                .map(HistoricActivityInstance::getActivityId)
                .collect(Collectors.toList());

        // 获取所有节点信息
        Map<String, ActivityInfoVO> activityInfoMap = new HashMap<>();
        List<HistoricActivityInstance> allActivities = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processId)
                .orderByHistoricActivityInstanceStartTime()
                .asc()
                .list();

        for (HistoricActivityInstance activity : allActivities) {
            ActivityInfoVO activityInfo = ActivityInfoVO.builder()
                    .activityId(activity.getActivityId())
                    .activityName(activity.getActivityName())
                    .activityType(activity.getActivityType())
                    .assignee(activity.getAssignee())
                    .startTime(activity.getStartTime() != null ? 
                            LocalDateTime.ofInstant(activity.getStartTime().toInstant(), ZoneId.systemDefault()) : null)
                    .endTime(activity.getEndTime() != null ? 
                            LocalDateTime.ofInstant(activity.getEndTime().toInstant(), ZoneId.systemDefault()) : null)
                    .durationInMillis(activity.getDurationInMillis())
                    .status(activity.getEndTime() != null ? "completed" : "active")
                    .build();
            activityInfoMap.put(activity.getActivityId(), activityInfo);
        }

        // 获取当前任务节点
        Task currentTask = taskService.createTaskQuery()
                .processInstanceId(processId)
                .singleResult();

        return ProcessDiagramVO.builder()
                .diagramBase64(base64Image)
                .activeActivityIds(activeActivityIds)
                .completedActivityIds(completedActivityIds)
                .activityInfoMap(activityInfoMap)
                .processInstanceId(processId)
                .currentTaskId(currentTask != null ? currentTask.getId() : null)
                .build();
    }

    @Override
    public List<String> getProcessNodeInfo(String processId) {
        // 获取流程定义ID
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processId)
                .singleResult();
        
        if (processInstance == null) {
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(processId)
                    .singleResult();
            if (historicProcessInstance == null) {
                throw new BusinessException("流程实例不存在");
            }
        }

        // 获取所有节点信息
        List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processId)
                .orderByHistoricActivityInstanceStartTime()
                .asc()
                .list();

        return activities.stream()
                .map(activity -> String.format("%s[%s] - %s - %s",
                        activity.getActivityName(),
                        activity.getActivityType(),
                        activity.getAssignee(),
                        activity.getEndTime() != null ? "已完成" : "进行中"))
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public void completeTask(String userId, String taskId) {
        Task task = getTask(taskId);
        checkTaskPermission(task, userId);
        taskService.complete(taskId);
    }

    private Task getTask(String taskId) {
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        if (task == null) {
            throw new BusinessException("任务不存在");
        }
        return task;
    }

    private void checkTaskPermission(Task task, String userId) {
        boolean hasPermission = userId.equals(task.getAssignee()) || 
            taskService.createTaskQuery()
                .taskId(task.getId())
                .taskCandidateUser(userId)
                .count() > 0;
                
        if (!hasPermission) {
            throw new BusinessException("无权处理该任务");
        }
    }

    private void updateExpenseStatus(String processInstanceId, ExpenseStatus status) {
        Expense expense = expenseMapper.findByProcessInstanceId(processInstanceId);
        if (expense == null) {
            throw new BusinessException("报销记录不存在");
        }
        expenseMapper.updateStatus(expense.getId(), status.getCode());
    }
} 